/**
 * 判断是否是外部链接
 *
 * @param {string} path
 * @returns {Boolean}
 */
export const isExternal = (path: string): boolean => {
  const reg =
    /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
  return reg.test(path)
}

/**
 * @description:  是否为数组
 */
export const isArray = (val: any): val is Array<any> => {
  return val && Array.isArray(val)
}

export const isString = (val: unknown): val is string => {
  return Object.prototype.toString.call(val) === '[object String]'
}

/**
 * @description 是否是有效的数字（包含正负整数，0以及正负浮点数）
 */
export const isNumber = (val: string) => {
  const regPos = /^\d+(\.\d+)?$/ // 非负浮点数
  const regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/ // 负浮点数
  if (regPos.test(val) || regNeg.test(val)) {
    return true
  } else {
    return false
  }
}

export const generateUUID = () => {
  let uuid = ''
  for (let i = 0; i < 32; i++) {
    const random = (Math.random() * 16) | 0
    if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-'
    uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16)
  }
  return uuid
}

/**
 * @description 处理 prop，当 prop 为多级嵌套时 ==> 返回最后一级 prop
 * @param {String} prop 当前 prop
 * @returns {String}
 * */
export const handleProp = (form: Record<string, any>, prop: string, valueFormat: 'default' | 'string' | 'number' | 'boolean' = 'default') => {
  let value: any
  if (!prop.includes('.')) value = form[prop]
  else {
    prop.split('.').forEach(item => (form = form[item] ?? ''))
    // 如果是 ElInputNumber，则需要返回数字类型，因此这里 form 如果为 ""，则返回 undefined，这样字符串和数字类型的组件都支持
    value = form || undefined
  }

  if (valueFormat === 'string' && value) return value + ''
  if (valueFormat === 'number' && value) return Number(value)
  if (valueFormat === 'boolean' && value) {
    if ((value as any) === '1' || (value as any) === 1) return true
    else return false
  }
  return value
}

export const getProp = (prop: string) => {
  const fieldArr = prop.split('.')
  if (fieldArr.length == 1) return prop
  return fieldArr[fieldArr.length - 1]
}

/**
 * @description 对 form 对象的 prop 赋值
 */
export const handleFormProp = (form: Record<string, any>, prop: string, value: any) => {
  if (!form) return
  const props = prop.split('.')
  let current = form

  for (let i = 0; i < props.length - 1; i++) {
    const prop = props[i]
    if (!current[prop]) current[prop] = {}
    current = current[prop]
  }
  current[props[props.length - 1]] = value
}

/**
 * 删除对象中的属性，包括嵌套属性
 * @param obj 对象
 * @param prop 对象的key
 */
export const deleteObjProperty = (obj: Record<string, any>, prop: string) => {
  const keys = prop.split('.')
  let current = obj
  for (let i = 0; i < keys.length; i++) {
    if (i === keys.length - 1) {
      if (current[keys[i]] !== undefined) {
        delete current[keys[i]]
        return true // 属性存在并已删除
      } else return false // 目标属性不存在
    }
    if (current[keys[i]] === undefined) return false // 路径中的某个部分不存在
    current = current[keys[i]]
  }
  return false // 不应该到达这里，除非路径为空
}

/**
 * @description 补 px 单位
 * @param val 值
 * @returns 补 px 单位的 string
 */
export const getPx = (val: number | string | undefined) => {
  if (!val) return ''
  if (isString(val)) {
    if (val.endsWith('%')) return val
    if (isNumber(val)) return `${val}px`
    return val
  }
  return `${val}px`
}
export const handleRowAccordingToProp = (row: { [key: string]: any }, prop: string) => {
  if (!prop.includes('.')) return row[prop] ?? '--'
  prop.split('.').forEach(item => (row = row[item] ?? '--'))
  return row
}

/**
 * @description 递归查找 callValue 对应的 enum 值
 * */
export const findItemNested = (enumData: any, callValue: any, value: string, children: string) => {
  return enumData.reduce((accumulator: any, current: any) => {
    if (accumulator) return accumulator
    // 兼容数字和字符串数字匹配，如值为数字 0，字典是 '0'，依然满足条件
    if (current[value] === callValue || current[value] === callValue + '') return current
    if (current[children]) return findItemNested(current[children], callValue, value, children)
  }, null)
}

/**
 * @description 根据枚举列表查询当需要的数据（如果指定了 value 的 key 值，会自动识别格式化）
 * @param {String} callValue 当前单元格值
 * @param {Array} enumData 字典列表
 * @param {Array} fieldNames label && value && children 的 key 值
 * @param {String} type 过滤类型
 * @returns {String}
 * */
export const filterEnum = (callValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: 'tag') => {
  const value = fieldNames?.value ?? 'value'
  const children = fieldNames?.children ?? 'children'
  const filterDataArray: Record<string, any>[] = []
  let filterData: Record<string, any> = {}
  // 判断 enumData 是否为数组
  if (Array.isArray(enumData)) {
    if (Array.isArray(callValue)) {
      callValue.forEach(item => {
        const data = findItemNested(enumData, item, value, children)
        data && filterDataArray.push(data)
      })
    } else filterData = findItemNested(enumData, callValue, value, children)
  }
  return filterDataArray.length ? filterDataArray : filterData || ''
}

/**
 * @description 根据枚举列表查询当需要的数据（如果指定了 label 的 key 值，会自动识别格式化）
 * @param {String} callValue 当前单元格值
 * @param {Array} enumData 字典列表
 * @param {Array} fieldNames label && value && children 的 key 值
 * @param {String} type 过滤类型（目前只有 tag）
 * @returns {String}
 * */
export const filterEnumLabel = (enumData: any, fieldNames?: FieldNamesProps, type?: 'tag') => {
  const label = fieldNames?.label ?? 'label'
  if (Array.isArray(enumData)) {
    const filterDataArray: any[] = []
    enumData.forEach((item: any) => filterDataArray.push(item[label]))
    return filterDataArray
  }
  return enumData ? enumData[label] : '--'
}

/**
 * @description 处理 ProTable 值为数组 || 无数据
 * @param {*} callValue 需要处理的值
 * @returns {String}
 * */
export const formatValue = (callValue: any) => {
  // 如果当前值为数组，使用 / 拼接（根据需求自定义）
  if (isArray(callValue)) return callValue.length ? callValue.join(' / ') : '--'
  return callValue ?? '--'
}

/**
 * @description 深拷贝函数
 */
export const deepCloneTableRow = (obj: any, hash = new WeakMap()): Record<string, any> => {
  if (!obj || typeof obj !== 'object') return obj
  if (Array.isArray(obj)) return obj.map(item => deepCloneTableRow(item))

  if (hash.has(obj)) return hash.get(obj)

  const Constructor = obj.constructor

  if (Constructor === Date) return new Date(obj)
  if (Constructor === RegExp) return new RegExp(obj)

  const newObj = new Constructor()
  hash.set(obj, newObj)

  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      newObj[key] = deepCloneTableRow(obj[key], hash)
    }
  }

  return newObj
}
